//////////////////////////////////////////////////////////////////////////
// PeekPocket - the hassle-free WiFi scanner for Windows Mobile
// coded by dzolee
// http://dzolee.blogspot.com
//
// PPDlg - This is the main form
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PeekPocket.h"
#include "PPDlg.h"
#include "SettingsStorage.h"
#include "TextCache.h"

#include "log.h"

extern CSettingsStorage g_Settings;
extern CTextCache g_Texts;
extern CDataStorage g_APData;
extern CWifiPeek g_WifiPeek;

// CPPDlg dialog
bool g_bPopupMenuActive;

/////////////////////////////////////////////////////////////////////////
CPPDlg::CPPDlg(CWnd* pParent /*=NULL*/)
	: CColoredDlg(CPPDlg::IDD, pParent)
{
	m_bRunning=true;
	m_dwTickCounter=0;
}

//////////////////////////////////////////////////////////////////////////
CPPDlg::~CPPDlg()
{
}

/////////////////////////////////////////////////////////////////////////
void CPPDlg::DoDataExchange(CDataExchange* pDX)
{
	CColoredDlg::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TABCTRL, m_tab);
	DDX_Control(pDX, IDC_SUSRES, m_btnSusRes);
//	DDX_Control(pDX, IDC_APICON, m_apicon);
	DDX_Control(pDX, IDC_EXITBTN, m_btnExit);
}

BEGIN_MESSAGE_MAP(CPPDlg, CColoredDlg)
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_EXITBTN, &CPPDlg::OnBnClickedExit)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_SUSRES, &CPPDlg::OnBnClickedSuspendResume)
	ON_CBN_SELCHANGE(IDC_ADAPTERS_COMBO, &CPPDlg::OnCbnSelchangeAdaptersCombo)
	ON_WM_WININICHANGE()
END_MESSAGE_MAP()


// CPPDlg message handlers

//////////////////////////////////////////////////////////////////////////
BOOL CPPDlg::OnInitDialog()
{
WCHAR buf[1024];
DWORD dwSize, dwScanSpeed;
int sx, sy;

	CColoredDlg::OnInitDialog();

	g_bPopupMenuActive=false;

	//first check display size - will show warning on square screen or less than QVGA.
	sy = GetSystemMetrics(SM_CYSCREEN);
	sx = GetSystemMetrics(SM_CXSCREEN);

	if( (sy == sx) || (sx < 240 || sy < 240) )
	{
		AfxMessageBox(_T("This program does not support square screen or resolutions below QVGA."));
	}

	//create font for buttons/checkboxes
	m_Font.CreateFont(12, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("Tahoma"));

	//set dialog colors
	SetBkgColor(GetSysColor(COLOR_ACTIVECAPTION));
	SetFrgColor(GetSysColor(COLOR_CAPTIONTEXT));

	//init tab control styles
	m_tab.ModifyStyle(0, WS_VISIBLE | CTCS_TOP | CTCS_AUTOHIDEBUTTONS | CTCS_CONTAINER);	

	//need to set tab control sizes for correct display on wm6 qvga smartphone
	CRect rcTmp;

	rcTmp.SetRect(4, 6, 236, 178);
	m_tab.MoveWindow(&rcTmp);

	//setup tab control colors
	TabItemColors ticColors;

	//get colors..
	m_tab.GetColors(&ticColors);
	
	//modify them..
	ticColors.crBkgActive=RGB_AVERAGE(GetSysColor(COLOR_ACTIVECAPTION), RGB(255, 255, 255));
	ticColors.crTxtActive=RGB(0, 0, 0);
	ticColors.crBkgInactive=GetSysColor(COLOR_ACTIVECAPTION);
	ticColors.crTxtInactive=RGB_AVERAGE(GetSysColor(COLOR_ACTIVECAPTION), GetSysColor(COLOR_CAPTIONTEXT));
	ticColors.crWndBkg=GetSysColor(COLOR_ACTIVECAPTION);
	
	//..and write back
	m_tab.SetColors(&ticColors);

	//create tabs and setup page colors

	//create the scanner dlg
	m_pScannerDlg=new CScannerDlg();
	m_pScannerDlg->Create(CScannerDlg::IDD, this);

	//..setup colors
	m_pScannerDlg->SetBkgColor(RGB_AVERAGE(GetSysColor(COLOR_ACTIVECAPTION), RGB(255, 255, 255)));
	m_pScannerDlg->SetFrgColor(RGB(0, 0, 0));
	//..add tab to tab control & dialog to container with the same call
	m_tab.AddDialog(0, g_Texts.GetString(teScanner), m_pScannerDlg);

	//create the options dlg
	m_pOptionsDlg=new COptionsDlg();
	m_pOptionsDlg->Create(COptionsDlg::IDD, this);
	//..setup colors
	m_pOptionsDlg->SetBkgColor(RGB_AVERAGE(GetSysColor(COLOR_ACTIVECAPTION), RGB(255, 255, 255)));
	m_pOptionsDlg->SetFrgColor(RGB(0, 0, 0));
	//..add tab to tab control & dialog to container with the same call
	m_tab.AddDialog(1, g_Texts.GetString(teOptions), m_pOptionsDlg);

	//init buttons

	//init exit btn
	m_btnExit.SetBkgIdleColor(GetSysColor(COLOR_ACTIVECAPTION));
	m_btnExit.SetBkgActiveColor(GetSysColor(COLOR_ACTIVECAPTION));
	m_btnExit.SetGradientColors(GetSysColor(COLOR_ACTIVECAPTION), RGB(200, 200, 200));
	m_btnExit.SetFlags(bfHGradient);
	m_btnExit.SetCaption(g_Texts.GetString(teExit));
	m_btnExit.SetFont(m_Font);

	//init suspend/resume btn
	m_btnSusRes.SetBkgIdleColor(GetSysColor(COLOR_ACTIVECAPTION));
	m_btnSusRes.SetBkgActiveColor(GetSysColor(COLOR_ACTIVECAPTION));
	m_btnSusRes.SetCaption(g_Texts.GetString(teSuspend), g_Texts.GetString(teResume));
	m_btnSusRes.SetGradientColors(GetSysColor(COLOR_ACTIVECAPTION), RGB(200, 200, 200));
	m_btnSusRes.SetFlags(bfHGradient);
	m_btnSusRes.ModifyStyle(0, BS_PUSHLIKE);
	m_btnSusRes.SetFont(m_Font);
	//init adapter list

	dwSize=sizeof(buf);
	if(false == g_WifiPeek.GetAdapters(buf, dwSize) || dwSize == 0)
	{
//		AfxMessageBox(_T("No adapters found!"));
	}
	else
	{
		//split adapter names, and add to combo

		CString strList, strName;
		int pos;

		strList=buf;
		pos=0;

		//just one adapter?
		if(strList.Find(_T(',')) == -1)
		{
			m_pOptionsDlg->m_adapters.AddString(strList);	
		}
		else
		//no, a list of adapters
		{
			do
			{
				strName=SplitString(strList, _T(','), pos);
				m_pOptionsDlg->m_adapters.AddString(strName);
			}while(strName != _T("") && pos != -1);
		}
		m_pOptionsDlg->m_adapters.SetCurSel(0);
		m_pOptionsDlg->UpdateData(FALSE);
	}

	//whether or not an adapter was found, update stored adapter name string
	OnCbnSelchangeAdaptersCombo();

	PositionControls();

	g_APData.Clear();

	//load bitmaps
	m_bmpAP.LoadBitmap(IDB_AP);
	m_bmpErr.LoadBitmap(IDB_ERR);

	//launch timers
	dwScanSpeed=g_Settings.GetScanSpeed();

	SetTimer(REFRESH_TIMER_ID, dwScanSpeed, 0);
	SetTimer(ANIM_TIMER_ID, dwScanSpeed/2, 0);
	SetTimer(MAINTENANCE_TIMER_ID, 60 * 1000, 0);	//one minute

	m_dwTickCounter=0;
	return TRUE;  // return TRUE unless you set the focus to a control
}

//////////////////////////////////////////////////////////////////////////
void CPPDlg::OnTimer(UINT nIDEvent)
{
DWORD dwScanSpeed;
CString strCurrentAdapter;

	//ignore timers if popup menu is currently active	
	if(g_bPopupMenuActive == true)
	{
		return;
	}

	dwScanSpeed=g_Settings.GetScanSpeed();
	g_Settings.GetString(sseAdapter, strCurrentAdapter);

	//temporarily disable timer
	KillTimer(nIDEvent);

	switch(nIDEvent)
	{
		case ANIM_TIMER_ID:
		{
			SystemIdleTimerReset();

			m_dwTickCounter++;

			//flash the icon
			if(m_dwTickCounter % 2)
			{
				CDC *pDC;

				pDC=GetDC();
				if(pDC != NULL)
				{
					//visible in this cycle
					if(strCurrentAdapter != _T(""))
					{
						//if have adapter name then it's OK
						m_bmpAP.DisplayTransparent(GetDC(), m_ptFlashImage.x, m_ptFlashImage.y, RGB(255, 255, 255));
					}
					else
					{
						//problem!
						m_bmpErr.DisplayTransparent(GetDC(), m_ptFlashImage.x, m_ptFlashImage.y, RGB(255, 255, 255));
					}
					//free up the DC
					ReleaseDC(pDC);
				}//if DC ok
			}
			else
			{
				//invisible in this cycle
				CRect rc;

				rc.SetRect(m_ptFlashImage.x, m_ptFlashImage.y, m_ptFlashImage.x+42, m_ptFlashImage.y+42);
				InvalidateRect(rc);
			}
			//restart this timer, with current refresh rate
			SetTimer(ANIM_TIMER_ID, dwScanSpeed/2, NULL);

			break;
		}
		case REFRESH_TIMER_ID:
		{
			DWORD dwTmp, dwCount;
			BSSIDInfo nfo[128];

			dwTmp=sizeof(nfo);
			dwCount=0;

			//get BSSID list
			if(g_WifiPeek.GetBSSIDs(strCurrentAdapter.GetBuffer(), nfo, dwTmp, dwCount) && dwCount > 0)
			{
				g_APData.ProcessInput(nfo, dwCount);
			}//get BSSID ok

			//initiate next scan, so adapter has some time to fill buffer - tip by Tom Lawton
			g_WifiPeek.RefreshBSSIDs(strCurrentAdapter.GetBuffer());

			//update list even if nothing found
			m_pScannerDlg->UpdateList();

			//restart timer
			SetTimer(REFRESH_TIMER_ID, dwScanSpeed, 0);

			break;
		}//case REFRESH_TIMER_ID
		case MAINTENANCE_TIMER_ID:
		{
			//get rid of old, timed-out names
			g_APData.DeleteOldDuplicates();

			//restart timer
			SetTimer(MAINTENANCE_TIMER_ID, 60 * 1000, 0);	//one minute
			break;
		}
	}//switch timer id
}

//////////////////////////////////////////////////////////////////////////
void CPPDlg::OnBnClickedExit()
{
	//shutdown timers
	KillTimer(REFRESH_TIMER_ID);
	KillTimer(ANIM_TIMER_ID);
	KillTimer(MAINTENANCE_TIMER_ID);
	EndDialog(IDOK);
}

//////////////////////////////////////////////////////////////////////////
//this is a CString::Tokenize replacement, it is not better, just it can
//be used on old MFC versions where the above call is missing.
CString CPPDlg::SplitString(CString strIn, TCHAR cSplitAt, int &iPos)
{
int i1, i2;

	if(iPos >= strIn.GetLength())
	{
		iPos=-1;
		return CString(_T(""));
	}

	i1=strIn.Find(cSplitAt, iPos);
	//not found?
	if(i1 == -1)
	{
		//return input string from iPos.
		i1=iPos;
		iPos=-1;
		return strIn.Mid(i1);
	}
	else
	{
		//found, return substring between (startpos, foundpos)
		i2=iPos;
		iPos=i1+1;

		if(i1 > i2)
		{
			return strIn.Mid(i2, i1-i2);
		}
		else
		{
			return strIn.Mid(i2);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
//handle suspend/resume
void CPPDlg::OnBnClickedSuspendResume()
{
DWORD dwScanSpeed;

	dwScanSpeed=g_Settings.GetScanSpeed();

	if(m_bRunning)
	{
		m_bRunning=false;
		KillTimer(REFRESH_TIMER_ID);
		KillTimer(ANIM_TIMER_ID);
	}
	else
	{
		m_bRunning=true;
		//restart timer
		SetTimer(REFRESH_TIMER_ID, dwScanSpeed, 0);
		SetTimer(ANIM_TIMER_ID, dwScanSpeed/2, 0);
		m_dwTickCounter=0;
	}
}

//////////////////////////////////////////////////////////////////////////
void CPPDlg::OnCbnSelchangeAdaptersCombo()
{
CString strTemp;

	//store current adapter name
	m_pOptionsDlg->m_adapters.GetWindowText(strTemp);
	g_Settings.SetString(sseAdapter, strTemp);
}

//////////////////////////////////////////////////////////////////////////
void CPPDlg::PositionControls()
{
	//position controls according to current screen orientation
RECT rcWA;
CRect rcControl;

	if(SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWA, 0))
	{
		//resize dialog
		MoveWindow(&rcWA);
		if(rcWA.right - rcWA.left > rcWA.bottom - rcWA.top)
		{
			//landscape positions
			rcControl.SetRect(244, 51, 244+60, 51+22);
			m_btnExit.MoveWindow(&rcControl);

			rcControl.SetRect(244, 136, 244+60, 136+22);
			m_btnSusRes.MoveWindow(&rcControl);

			m_ptFlashImage.x=254;
			m_ptFlashImage.y=83;
		}//if landscape
		else
		{
			//portrait positions
			rcControl.SetRect(20, 199, 20+60, 199+22);
			m_btnExit.MoveWindow(&rcControl);

			rcControl.SetRect(160, 199, 160+60, 199+22);
			m_btnSusRes.MoveWindow(&rcControl);

			m_ptFlashImage.x=97;
			m_ptFlashImage.y=189;
		}//else if portrait
	}//if get workarea ok
	Invalidate();

	//remove vertical scroll bar if added by the system
	//after switching from portrait to landscape
	LONG style;
	
	style=GetWindowLong(GetSafeHwnd(), GWL_STYLE);
	if(style & WS_VSCROLL)
	{
		style &= ~WS_VSCROLL;
		SetWindowLong(GetSafeHwnd(), GWL_STYLE, style);
	}
}

//////////////////////////////////////////////////////////////////////////
void CPPDlg::OnWinIniChange(LPCTSTR lpszSection)
{
	PositionControls();
}

